"""
Redis缓存服务模块
用于缓存LLM响应、TTS音频、视频下载等耗时操作
"""

import redis
import json
import hashlib
import logging
from typing import Optional, Any, Union
import pickle
import os

class CacheService:
    def __init__(self, host='localhost', port=6379, db=0, 
                 default_ttl=3600, max_retries=3):
        """
        初始化Redis缓存服务
        
        Args:
            host: Redis服务器地址
            port: Redis端口
            db: 数据库编号
            default_ttl: 默认过期时间（秒）
            max_retries: 连接重试次数
        """
        self.default_ttl = default_ttl
        self.max_retries = max_retries
        
        try:
            self.redis_client = redis.Redis(
                host=host, 
                port=port, 
                db=db,
                decode_responses=False,  # 支持二进制数据
                socket_timeout=5,
                socket_connect_timeout=5,
                retry_on_timeout=True
            )
            # 测试连接
            self.redis_client.ping()
            logging.info(f"Redis缓存服务已连接: {host}:{port}")
            self.connected = True
        except Exception as e:
            logging.warning(f"Redis连接失败: {e}，将使用无缓存模式")
            self.redis_client = None
            self.connected = False
    
    def _generate_key(self, prefix: str, *args) -> str:
        """
        生成缓存键
        
        Args:
            prefix: 键前缀
            *args: 用于生成键的参数
            
        Returns:
            缓存键字符串
        """
        # 将参数转换为字符串并计算哈希
        content = str(args)
        hash_key = hashlib.md5(content.encode()).hexdigest()[:12]
        return f"{prefix}:{hash_key}"
    
    def get(self, key: str) -> Optional[Any]:
        """
        从缓存获取数据
        
        Args:
            key: 缓存键
            
        Returns:
            缓存的数据，如果不存在则返回None
        """
        if not self.connected:
            return None
        
        try:
            data = self.redis_client.get(key)
            if data:
                return pickle.loads(data)
            return None
        except Exception as e:
            logging.warning(f"从缓存读取失败 {key}: {e}")
            return None
    
    def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """
        设置缓存数据
        
        Args:
            key: 缓存键
            value: 要缓存的数据
            ttl: 过期时间（秒），如果为None则使用默认值
            
        Returns:
            是否设置成功
        """
        if not self.connected:
            return False
        
        try:
            ttl = ttl or self.default_ttl
            serialized_data = pickle.dumps(value)
            return self.redis_client.setex(key, ttl, serialized_data)
        except Exception as e:
            logging.warning(f"设置缓存失败 {key}: {e}")
            return False
    
    def exists(self, key: str) -> bool:
        """
        检查缓存键是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        if not self.connected:
            return False
        
        try:
            return bool(self.redis_client.exists(key))
        except Exception as e:
            logging.warning(f"检查缓存存在性失败 {key}: {e}")
            return False
    
    def delete(self, key: str) -> bool:
        """
        删除缓存数据
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        if not self.connected:
            return False
        
        try:
            return bool(self.redis_client.delete(key))
        except Exception as e:
            logging.warning(f"删除缓存失败 {key}: {e}")
            return False
    
    def clear_prefix(self, prefix: str) -> int:
        """
        清除指定前缀的所有缓存
        
        Args:
            prefix: 键前缀
            
        Returns:
            删除的键数量
        """
        if not self.connected:
            return 0
        
        try:
            pattern = f"{prefix}:*"
            keys = self.redis_client.keys(pattern)
            if keys:
                return self.redis_client.delete(*keys)
            return 0
        except Exception as e:
            logging.warning(f"清除前缀缓存失败 {prefix}: {e}")
            return 0
    
    def get_stats(self) -> dict:
        """
        获取缓存统计信息
        
        Returns:
            包含缓存统计信息的字典
        """
        if not self.connected:
            return {"connected": False}
        
        try:
            info = self.redis_client.info()
            return {
                "connected": True,
                "used_memory": info.get("used_memory_human", "Unknown"),
                "total_connections": info.get("total_connections_received", 0),
                "keyspace_hits": info.get("keyspace_hits", 0),
                "keyspace_misses": info.get("keyspace_misses", 0)
            }
        except Exception as e:
            logging.warning(f"获取缓存统计失败: {e}")
            return {"connected": False, "error": str(e)}

# 缓存装饰器
def cache_result(prefix: str, ttl: Optional[int] = None, key_func=None):
    """
    缓存函数结果的装饰器
    
    Args:
        prefix: 缓存键前缀
        ttl: 过期时间（秒）
        key_func: 自定义键生成函数，接收函数参数并返回键的一部分
        
    Usage:
        @cache_result("llm_response", ttl=1800)
        def call_llm(prompt):
            # LLM调用逻辑
            pass
    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            # 生成缓存键
            if key_func:
                key_parts = key_func(*args, **kwargs)
            else:
                key_parts = (args, tuple(sorted(kwargs.items())))
            
            cache_key = cache_service._generate_key(prefix, *key_parts)
            
            # 尝试从缓存获取
            cached_result = cache_service.get(cache_key)
            if cached_result is not None:
                logging.info(f"从缓存获取结果: {prefix}")
                return cached_result
            
            # 执行函数并缓存结果
            logging.info(f"执行函数并缓存结果: {prefix}")
            result = func(*args, **kwargs)
            
            if result is not None:
                cache_service.set(cache_key, result, ttl)
            
            return result
        return wrapper
    return decorator

# 全局缓存服务实例
cache_service = CacheService()

# 预定义的缓存键前缀
class CacheKeys:
    LLM_RESPONSE = "llm_response"
    TTS_AUDIO = "tts_audio"
    VIDEO_DOWNLOAD = "video_download"
    KNOWLEDGE_CARDS = "knowledge_cards"
    CHAPTERS = "chapters"
    SUMMARY_KEYWORDS = "summary_keywords"
    YOUTUBE_UPLOAD = "youtube_upload"
    WHISPER_TRANSCRIPT = "whisper_transcript"